package reports

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"

	"github.com/tealeg/xlsx"
)

//Xlsx excel导出类
type Xlsx struct {
	sheet    *xlsx.Sheet
	file     *xlsx.File
	style    *xlsx.Style
	columns  []Column
	colStart int
	leafs    []Column
}

//NewXlsx 新建excel导出类
func NewXlsx() (*Xlsx, error) {
	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		return nil, err
	}
	style := xlsx.Style{
		Border: *xlsx.NewBorder("thin", "thin", "thin", "thin"),
		Font:   *xlsx.NewFont(12, "宋体"),
	}
	style.Alignment.Horizontal = "center"
	style.Alignment.Vertical = "center"
	style.Font.Bold = false
	return &Xlsx{
		file:  file,
		sheet: sheet,
		style: &style,
	}, nil
}
func (x *Xlsx) SetColStart(colStart int) {
	for i := 0; i < colStart; i++ {
		x.sheet.AddRow()
	}
	x.colStart = colStart
}

//Export 导出Xlsx数据
func (x *Xlsx) Export() *bytes.Buffer {
	buff := new(bytes.Buffer)
	x.file.Write(buff)
	return buff
}
func (x *Xlsx) AddTitle(title string, rowNum int) {
	row := x.sheet.Rows[rowNum-1]
	cell := row.AddCell()
	style := xlsx.Style{
		Border: *xlsx.NewBorder("thin", "thin", "thin", "thin"),
		Font:   *xlsx.NewFont(14, "黑体"),
	}
	style.Alignment.Horizontal = "center"
	style.Alignment.Vertical = "center"
	style.Font.Bold = true
	colNums := len(x.leafs)
	for i := 1; i < colNums; i++ {
		row.AddCell().SetStyle(&style)
	}
	cell.Merge(colNums-1, 0)
	cell.SetString(title)
	cell.SetStyle(&style)
}
func (x *Xlsx) AddSubhead(subheadStr string, rowNum int) {
	row := x.sheet.Rows[rowNum-1]
	cell := row.AddCell()
	cell.SetStyle(x.style)
	colNums := len(x.leafs)
	style := xlsx.Style{
		Border: *xlsx.NewBorder("thin", "thin", "thin", "thin"),
		Font:   *xlsx.NewFont(12, "黑体"),
	}
	style.Alignment.Horizontal = "Left"
	style.Alignment.Vertical = "center"
	style.Font.Bold = false
	for i := 1; i < colNums; i++ {
		row.AddCell().SetStyle(x.style)
	}
	cell.Merge(colNums-1, 0)
	cell.SetString(subheadStr)
}

//AddColumns 添加列头
func (x *Xlsx) AddColumns(columns []Column) {
	maxDeep := 0
	for _, col := range columns {
		deep := x.getColumnDeep(&col)
		if deep > maxDeep {
			maxDeep = deep
		}
	}
	for i := 0; i < maxDeep; i++ {
		x.sheet.AddRow()
	}
	leafs := make([]Column, 0, 20)
	var loop func(*Column, int) int
	loop = func(col *Column, level int) (count int) {
		clen := len(col.Children)
		var rowSpan, colSpan, rowNum int
		if clen > 0 {
			for _, cc := range col.Children {
				count += loop(&cc, level+1)
			}
			rowSpan = count - 1
			colSpan = 0
		} else {
			rowSpan = 0
			colSpan = maxDeep - level
			leafs = append(leafs, *col)
			count = 1
		}
		rowNum = level - 1 + x.colStart
		row := x.sheet.Rows[rowNum]
		cell := row.AddCell()
		for i := 1; i <= colSpan; i++ {
			row = x.sheet.Rows[rowNum+i]
			row.AddCell().SetStyle(x.style)
		}
		for i := 1; i <= rowSpan; i++ {
			row.AddCell().SetStyle(x.style)
		}
		cell.Merge(rowSpan, colSpan)
		cell.SetValue(col.Title)
		cell.SetStyle(x.style)
		return
	}
	for _, col := range columns {
		loop(&col, 1)
	}
	x.columns = columns
	x.leafs = leafs
}

//AddRow 导出行
func (x *Xlsx) AddRow(rowData map[string]string) error {
	row := x.sheet.AddRow()
	for _, col := range x.leafs {
		cell := row.AddCell()
		style := xlsx.Style{
			Border: *xlsx.NewBorder("thin", "thin", "thin", "thin"),
			Font:   *xlsx.NewFont(12, "宋体"),
		}
		if col.DataType == "float" || col.DataType == "int" || col.DataType == "number" {
			style.Alignment.Horizontal = "right"
			numVal := rowData[col.DataIndex]
			if numVal != "" {
				if col.DataType == "float" || col.DataType == "number" {
					format := col.Format
					isP := strings.HasSuffix(format, "f%")
					fVal, err := strconv.ParseFloat(numVal, 64)
					if err != nil {
						return fmt.Errorf("报表列%s类型为number，值%s无效！", col.Name, numVal)
					}
					cell.SetFloat(fVal)
					if format == "" {
						format = ".2f"
					}
					index := len(format) - 1
					if isP {
						index--
					}
					zNumStr := string(([]byte(format))[1:index])
					zNum, err := strconv.Atoi(zNumStr)
					if err != nil {
						return fmt.Errorf("报表列%s的format定义的%s是无效！", col.Name, format)
					}
					zNumArr := make([]string, zNum)
					for i := 0; i < zNum; i++ {
						zNumArr[i] = "0"
					}
					zeroExp := strings.Join(zNumArr, "")
					if isP {
						cell.NumFmt = fmt.Sprintf("0.%s", zeroExp) + "%"
					} else {
						cell.NumFmt = fmt.Sprintf("#,##0.%s;[red]-#,##0.%s", zeroExp, zeroExp)
					}
				} else {
					dVal, err := strconv.Atoi(numVal)
					if err != nil {
						return fmt.Errorf("报表列%s类型为int，值%s无效！", col.Name, numVal)
					}
					cell.SetInt(dVal)
					cell.NumFmt = "#,##0 ;[red]-#,##0"
				}
			}
		} else {
			style.Alignment.Horizontal = "left"
			cell.SetValue(rowData[col.DataIndex])
		}
		cell.SetStyle(&style)
	}
	return nil
}
func (x *Xlsx) getColumnDeep(column *Column) int {
	cols := column.Children
	if len(cols) == 0 {
		return 1
	}
	deep := 0
	for _, col := range cols {
		d2 := x.getColumnDeep(&col)
		if d2 > deep {
			deep = d2
		}
	}
	return deep + 1
}
